/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package Practica5.practica;

import Practica5.util.Edge;
import Practica5.util.Graph;
import Practica5.util.Vertex;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author dinamica
 */
public class MyGraph<E> implements Graph<E> {
    
    private String fileName;
    private CopyOnWriteArrayList<Vertex<E>> vertex;
    private CopyOnWriteArrayList<Edge<E>> edges;
    private boolean directed;
    private Vertex<E>[][] adjacencyMatrix;
    private int indexEdge;
    private int indexVertex;
    
    public MyGraph(String fileName) {
        vertex = new CopyOnWriteArrayList<>();
        edges = new CopyOnWriteArrayList<>();
        directed = false;
        this.fileName = fileName;
        indexEdge = 0;
        indexVertex = 0;
        readFile();
    }
    
    private boolean readFile() {
        BufferedReader br = null;
        try {
            if (fileName.equals("")) {
                return false;
            }
            br = new BufferedReader(new FileReader(fileName));
            try {
                String line = br.readLine();
                if (!line.split(",")[0].trim().equals("graph") && !line.split(",")[1].trim().equals("directed")) {
                    return false;
                }
                if (line.split(",")[2].trim().equals("true")) {
                    directed = true;
                } else {
                    directed = false;
                }
                String[] lineSplit;
                while ((line = br.readLine()) != null) {
                    lineSplit = line.split(",");
                    switch (lineSplit[0].trim()) {
                        case "vertex":
                            boolean exists = false;
                            for (Vertex v : vertex) {
                                if (v.getElement().equals(lineSplit[2]) && v.getColor() == Integer.parseInt(lineSplit[4].trim())) {
                                    exists = true;
                                    break;
                                }
                            }
                            if (exists) {
                                break;
                            }
                            MyVertex v = new MyVertex(lineSplit[2].trim(), indexVertex++);
                            v.setColor(Integer.parseInt(lineSplit[4].trim()));
                            vertex.add(v);
                            
                            break;
                        case "edge":
                            MyVertex source = null,
                             target = null;
                            for (Vertex v1 : vertex) {
                                
                                if (((String) v1.getElement()).equals(lineSplit[2])) {
                                    source = (MyVertex) v1;
                                } else if (((String) v1.getElement()).equals(lineSplit[4])) {
                                    target = (MyVertex) v1;
                                }
                            }
                            if (directed) {
                                source.addNeighbour(target);
                                MyEdge e = new MyEdge(source, target, Integer.parseInt(lineSplit[6].trim()), indexEdge++, Integer.parseInt(lineSplit[8].trim()));
                                edges.add(e);
                                source.setExgrado(source.getExgrado() + 1);
                                target.setIngrado(target.getIngrado() + 1);
                            } else {
                                source.addSymmetricNeighbour(target);
                                MyEdge e1 = new MyEdge(source, target, Integer.parseInt(lineSplit[6].trim()), indexEdge++, Integer.parseInt(lineSplit[8].trim()));
                                edges.add(e1);
                                source.setExgrado(source.getExgrado() + 1);
                                source.setIngrado(source.getIngrado() + 1);
                                target.setIngrado(target.getIngrado() + 1);
                                target.setIngrado(target.getIngrado() + 1); 
                            }
                            break;
                    }
                }
                
            } catch (IOException ex) {
                Logger.getLogger(MyGraph.class.getName()).log(Level.SEVERE, null, ex);
            }
            
        } catch (FileNotFoundException ex) {
            Logger.getLogger(MyGraph.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            try {
                br.close();
            } catch (IOException ex) {
                Logger.getLogger(MyGraph.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        return true;
        
    }
    
    private void matrixFilled() {
        int i, j;
        
        if (directed) {
            adjacencyMatrix = new MyVertex[vertex.size()][vertex.size()];
            for (Vertex v : vertex) {
                i = v.getIndex();
                for (Vertex v1 : (List<Vertex>) v.getNeighboursList()) {
                    j = v1.getColor();
                    adjacencyMatrix[i][j] = v1;
                }
            }
        } else {
            adjacencyMatrix = new MyVertex[vertex.size()][vertex.size()];
            for (Vertex v : vertex) {
                i = v.getIndex();
                for (Vertex v1 : (List<Vertex>) v.getNeighboursList()) {
                    j = v1.getColor();
                    adjacencyMatrix[i][j] = v1;
                }
            }
        }
    }
    
    @Override
    public int getVertexNum() {
        return vertex.size();
    }
    
    @Override
    public int getEdgesNum() {
        return edges.size();
    }
    
    @Override
    public List getVertexList() {
        return vertex;
    }
    
    @Override
    public Vertex[] getVertexArray() {
        return (Vertex[]) vertex.toArray();
    }
    
    @Override
    public Vertex getVertex(int i) {
        return vertex.get(i);
    }
    
    @Override
    public List getEdgesList() {
        return edges;
    }
    
    @Override
    public Edge[] getEdgesArray() {
        return (Edge[]) edges.toArray();
    }
    
    @Override
    public Edge getEdge(Vertex start, Vertex end) {
        Edge e = null;
        if (directed) {
            for (Edge e1 : edges) {
                if (e1.getSource().equals(start) && e.getTarget().equals(end)) {
                    return e1;
                }
            }
        } else {
            for (Edge e1 : edges) {
                if (e1.getSource().equals(start) && e.getTarget().equals(end)) {
                    return e1;
                } else if (e1.getSource().equals(end) && e1.getTarget().equals(start)) {
                    e1.setSource(end);
                    e1.setTarget(start);
                    return e1;
                }
                
            }
        }
        return e;
    }
    
    @Override
    public Edge getEdge(int i) {
        return edges.get(i);
    }
    
    @Override
    public boolean isAdjacent(Vertex start, Vertex end) {
        for (Vertex v : (List<Vertex>) start.getNeighboursList()) {
            if (v.equals(end)) {
                return true;
            }
        }
        return false;
    }
    
    @Override
    public boolean isAdjacent(int i, int j) {
        Vertex v1 = vertex.get(i);
        Vertex v2 = vertex.get(j);
        Edge e = this.getEdge(v1, v2);
        if (e != null && (e.getSource().equals(v1) && e.getTarget().equals(v2) || e.getSource().equals(v2) && e.getTarget().equals(v1))) {
            return true;
        }
        return false;
    }
    
    @Override
    public int getDegree(Vertex v) {
        return v.grade();
    }
    
    @Override
    public int getDegree(int i) {
        return this.getVertex(i).grade();
    }
    
    @Override
    public int getWeight(Edge e) {
        return e.getWeight();
    }
    
    @Override
    public int getWeight(Vertex start, Vertex end) {
        return this.getEdge(start, end).getWeight();
    }
    
    @Override
    public int getWeight(int start, int end) {
        return this.getEdge(this.getVertex(start), this.getVertex(end)).getWeight();
    }
    
    @Override
    public Vertex[] getNeighborsArray(Vertex v) {
        return (Vertex[]) v.getNeighboursList().toArray();
    }
    
    @Override
    public Vertex[] getNeighborsArray(int i) {
        return (Vertex[]) this.getVertex(i).getNeighboursList().toArray();
    }
    
    @Override
    public List getNeighborsList(Vertex v) {
        return v.getNeighboursList();
    }
    
    @Override
    public List getNeighborsList(int i) {
        return this.getVertex(i).getNeighboursList();
    }
    
    public synchronized void removeVertex(Vertex v){
        MyVertex v1 = (MyVertex) v;
        if(directed){
            
            for(MyVertex vi: (List<MyVertex>)v1.getNeighboursList()){
                vi.setIngrado(vi.getIngrado()-1);
            }
            for(Edge e: edges){
                if(e.getSource().equals(v1) || e.getTarget().equals(v1)){
                    edges.remove(e);
                }
            }
        }else{
            for(MyVertex vi: (List<MyVertex>)v.getNeighboursList()){
                vi.setIngrado(vi.getIngrado()-1);
                vi.setExgrado(vi.getExgrado()-1);
                vi.getNeighboursList().remove(v);
            }
            for(Edge e: edges){
                if(e.getTarget().equals(v) || e.getSource().equals(v)){
                    edges.remove(e);
                }
            }
        }
        vertex.remove(v1);
        for(int i = 0; i<vertex.size();i++){
            vertex.get(i).setIndex(i);
        }
        for(int i=0;i<edges.size();i++){
            edges.get(i).setIndex(i);
        }
    }
    
   public Vertex getVertexIngradeZero(){
        for (Object v: (List) vertex){
            
            if(((MyVertex)v).getIngrado() == 0){
                return (MyVertex)v;
            }
        }
       return null;
   }
}
